import type { FuseResult, FuseResultMatch } from 'fuse.js'
import type { GetItemKeys } from '../types/utils'

function truncateHTMLFromStart(html: string, maxLength: number) {
  let truncated = ''
  let totalLength = 0
  let insideTag = false

  // Iterate through the HTML string in reverse order
  for (let i = html.length - 1; i >= 0; i--) {
    if (html[i] === '>') {
      insideTag = true
    } else if (html[i] === '<') {
      insideTag = false
      truncated = html[i] + truncated
      continue
    }

    if (!insideTag) {
      totalLength++
    }

    if (totalLength <= maxLength) {
      truncated = html[i] + truncated
    } else {
      // If we've reached the max length, we break out of the loop
      // to prevent further processing of the string
      truncated = '...' + truncated
      break
    }
  }

  return truncated
}

export function highlight<T>(item: T & { matches?: FuseResult<T>['matches'] }, searchTerm: string, forceKey?: GetItemKeys<T>, omitKeys?: GetItemKeys<T>[]) {
  function generateHighlightedText(value: FuseResultMatch['value'], indices: FuseResultMatch['indices'] = []) {
    value = value || ''
    let content = ''
    let nextUnhighlightedRegionStartingIndex = 0

    indices.forEach((region) => {
      // skip if region is a single character
      if (region.length === 2 && region[0] === region[1]) {
        return
      }

      const lastIndiceNextIndex = region[1] + 1
      const isMatched = (lastIndiceNextIndex - region[0]) >= searchTerm.length

      content += [
        value.substring(nextUnhighlightedRegionStartingIndex, region[0]),
        isMatched && `<mark>`,
        value.substring(region[0], lastIndiceNextIndex),
        isMatched && '</mark>'
      ].filter(Boolean).join('')

      nextUnhighlightedRegionStartingIndex = lastIndiceNextIndex
    })

    content += value.substring(nextUnhighlightedRegionStartingIndex)

    const markIndex = content.indexOf('<mark>')
    if (markIndex !== -1) {
      content = truncateHTMLFromStart(content, content.length - markIndex)
    }

    return content
  }

  if (!item.matches?.length) {
    return
  }

  for (const match of item.matches) {
    if (forceKey && match.key !== forceKey) {
      continue
    }
    if (omitKeys?.includes(match.key as GetItemKeys<T>)) {
      continue
    }

    return generateHighlightedText(match.value, match.indices)
  }
}
